﻿namespace PugLib
{
	using System;
	using System.Collections.Generic;
	using System.Linq.Expressions;
	using System.Reflection;

	public static class FunctionFactory
	{
		/// <summary>
		/// Creates a compiled delegate function for the specified type and method name
		/// </summary>
		/// <typeparam name="TFunc">Delegate function to create</typeparam>
		/// <param name="value">Constant to get method from</param>
		/// <param name="methodName">Method to examine</param>
		/// <returns>Delegate function of the specified methodName</returns>
		/// <see cref="http://www.kowitz.net/lambda-function-factory"/>
		public static TFunc CreateFunc<TFunc>(Object value, String methodName)
		{
			List<ParameterExpression> args = new List<ParameterExpression>();

			Type targetType = value.GetType();
			MethodInfo minfo = targetType.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public);

			if (minfo != null)
			{
				ConstantExpression target = Expression.Constant(value);
				foreach (ParameterInfo arg in minfo.GetParameters())
				{
					args.Add(Expression.Parameter(arg.ParameterType, arg.Name));
				}
				MethodCallExpression methodinvokeExpression = Expression.Call(target, minfo, args.ToArray());
				Expression<TFunc> lambda = Expression.Lambda<TFunc>(methodinvokeExpression, args.ToArray());

				// now the following Lambda is created:
				// (TArg1, TArg2) => value.MethodName(TArg1, TArg2);

				return lambda.Compile();
			}
			return default(TFunc);
		}
	}
}